package com.yf.exam.modules.qu.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
import com.yf.exam.ability.upload.dto.UploadRespDTO;
import com.yf.exam.ability.upload.service.UploadService;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.Base64DecodedMultipartFile;
import com.yf.exam.core.utils.BeanMapper;
import com.yf.exam.core.utils.excel.ImportExcel;
import com.yf.exam.modules.paper.dto.ApplyLicense;
import com.yf.exam.modules.paper.dto.ExamApplyImportTemplateDTO;
import com.yf.exam.modules.paper.dto.SysExamApply;
import com.yf.exam.modules.paper.entity.ExamApply;
import com.yf.exam.modules.paper.service.ExamApplyService;
import com.yf.exam.modules.paper.service.PaperService;
import com.yf.exam.modules.qu.dto.QuAnswerDTO;
import com.yf.exam.modules.qu.dto.QuDTO;
import com.yf.exam.modules.qu.dto.export.QuExportDTO;
import com.yf.exam.modules.qu.dto.ext.QuDetailDTO;
import com.yf.exam.modules.qu.dto.request.QuQueryReqDTO;
import com.yf.exam.modules.qu.entity.Qu;
import com.yf.exam.modules.qu.entity.QuAnswer;
import com.yf.exam.modules.qu.entity.QuRepo;
import com.yf.exam.modules.qu.enums.QuType;
import com.yf.exam.modules.qu.mapper.QuMapper;
import com.yf.exam.modules.qu.service.QuAnswerService;
import com.yf.exam.modules.qu.service.QuRepoService;
import com.yf.exam.modules.qu.service.QuService;
import com.yf.exam.modules.qu.utils.ImageCheckUtils;
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
import com.yf.exam.modules.sys.user.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.usermodel.*;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.yf.exam.core.utils.DateUtils.parseDate;

/**
 * <p>
 * 语言设置 服务实现类
 * </p>
 *
 * @author 聪明笨狗
 * @since 2020-05-25 10:17
 */
@Slf4j
@Service
public class QuServiceImpl extends ServiceImpl<QuMapper, Qu> implements QuService {
    @Autowired
    private PaperService baseService;
    @Autowired
    private QuAnswerService quAnswerService;

    @Autowired
    private UploadService uploadService;

    @Autowired
    private SysUserService sysUserservice;
    @Autowired
    private QuRepoService quRepoService;

    @Autowired
    private ImageCheckUtils imageCheckUtils;
    @Autowired
    private ExamApplyService examApplyService;

//    private Map<String, PictureData> picMap = new HashMap<>();

    @Override
    public IPage<QuDTO> paging(PagingReqDTO<QuQueryReqDTO> reqDTO) {
        //创建分页对象
        Page page = reqDTO.toPage();

        //转换结果
        IPage<QuDTO> pageData = baseMapper.paging(page, reqDTO.getParams());
        return pageData;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<String> ids) {
        // 移除题目
        this.removeByIds(ids);

        // 移除选项
        QueryWrapper<QuAnswer> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(QuAnswer::getQuId, ids);
        quAnswerService.remove(wrapper);

        // 移除题库绑定
        QueryWrapper<QuRepo> wrapper1 = new QueryWrapper<>();
        wrapper1.lambda().in(QuRepo::getQuId, ids);
        quRepoService.remove(wrapper1);
    }

    @Override
    public List<Qu> listByRandom(String repoId, Integer quType, List<String> excludes, Integer size) {
        return baseMapper.listByRandom(repoId, quType, excludes, size);
    }

    @Override
    public QuDetailDTO detail(String id) {

        QuDetailDTO respDTO = new QuDetailDTO();
        Qu qu = this.getById(id);
        BeanMapper.copy(qu, respDTO);

        List<QuAnswerDTO> answerList = quAnswerService.listByQu(id);
        respDTO.setAnswerList(answerList);

        List<String> repoIds = quRepoService.listByQu(id);
        respDTO.setRepoIds(repoIds);

        return respDTO;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(QuDetailDTO reqDTO) {


        // 校验数据
        this.checkData(reqDTO, "");

        Qu qu = new Qu();
        BeanMapper.copy(reqDTO, qu);

        // 校验图片地址
        imageCheckUtils.checkImage(qu.getImage(), "题干图片地址错误！");

        // 更新
        this.saveOrUpdate(qu);

        // 保存全部问题
        quAnswerService.saveAll(qu.getId(), reqDTO.getAnswerList());

        // 保存到题库
        quRepoService.saveAll(qu.getId(), qu.getQuType(), reqDTO.getRepoIds());

    }

    @Override
    public List<QuExportDTO> listForExport(QuQueryReqDTO query) {
        return baseMapper.listForExport(query);
    }

    @Override
    public int importExcel(List<QuExportDTO> dtoList) {

        //根据题目名称分组
        Map<Integer, List<QuExportDTO>> anMap = new HashMap<>(16);

        //题目本体信息
        Map<Integer, QuExportDTO> quMap = new HashMap<>(16);

        //数据分组
        for (QuExportDTO item : dtoList) {

            // 空白的ID
            if (StringUtils.isEmpty(item.getNo())) {
                continue;
            }

            Integer key;
            //序号
            try {
                key = Integer.parseInt(item.getNo());
            } catch (Exception e) {
                continue;
            }

            //如果已经有题目了，直接处理选项
            if (anMap.containsKey(key)) {
                anMap.get(key).add(item);
            } else {
                //如果没有，将题目内容和选项一起
                List<QuExportDTO> subList = new ArrayList<>();
                subList.add(item);
                anMap.put(key, subList);
                quMap.put(key, item);
            }
        }

        int count = 0;
        try {

            //循环题目插入
            for (Integer key : quMap.keySet()) {

                QuExportDTO im = quMap.get(key);

                //题目基本信息
                QuDetailDTO qu = new QuDetailDTO();
                qu.setContent(im.getQContent());
                qu.setAnalysis(im.getQAnalysis());
                qu.setQuType(Integer.parseInt(im.getQuType()));
                qu.setCreateTime(new Date());

                //设置回答列表
                List<QuAnswerDTO> answerList = this.processAnswerList(anMap.get(key));
                //设置题目
                qu.setAnswerList(answerList);
                //设置引用题库
                qu.setRepoIds(im.getRepoList());
                // 保存答案
                this.save(qu);
                count++;
            }

        } catch (ServiceException e) {
            e.printStackTrace();
            throw new ServiceException(1, "导入出现问题，行：" + count + "，" + e.getMessage());
        }

        return count;
    }


    /**
     * 获取当前登录用户
     *
     * @return
     */
    public SysUserLoginDTO getLoginUser() {
        try {
            return SecurityUtils.getSubject().getPrincipal() != null ?
                    (SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal() : null;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public void importExcelApply(MultipartFile file) throws IOException, InvalidFormatException, InstantiationException, IllegalAccessException {
        // 全局提取一次图片
        // 全局提取一次图片
        XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        // 保存成全局变量供 uploadImageFromExcel 使用
        // 2. 提取图片到 map
        Map<String, XSSFPictureData> picMap = extractPictures(sheet);
        picMap.keySet().forEach(pic -> log.error("图片标志：{}", pic));
        ImportExcel ei = new ImportExcel(file, 1, 0);
        List<ExamApplyImportTemplateDTO> list = ei.getDataList(ExamApplyImportTemplateDTO.class);
        for (int i = 0; i < list.size(); i++) {
            ExamApplyImportTemplateDTO dto = list.get(i);
            final int rowIndex = i + 2; // Excel从第3行开始导入数据
            SysExamApply apply = new SysExamApply();

            apply.setUserName(dto.getPhone());
            apply.setRealName(dto.getRealName());
            apply.setPhone(dto.getPhone());
            apply.setIdCard(dto.getIdCard());
            apply.setCompanyName(dto.getCompanyName());
            apply.setJobType(dto.getJobType());
            apply.setDeptId("1");

            // 处理时间
            apply.setEntryTime(parseDate(dto.getEntryTime()));
            apply.setLeaveTime(parseDate(dto.getLeaveTime()));

            apply.setLicenses(new ArrayList<ApplyLicense>() {
                {   // 设置证书信息
                    ApplyLicense license = new ApplyLicense();
                    license.setType(dto.getLicenseType());
                    license.setProject(dto.getLicenseProject());
                    license.setNumber(dto.getLicenseNumber());
                    license.setIssueDate(dto.getLicenseIssueDate());
                    license.setExpireDate(dto.getLicenseExpireDate());
                    license.setName(dto.getLicenseName()); // 或从Excel中获取其他字段
                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(license.getType())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license.getProject())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license.getName())
                    ) {
                        license.setImgUrl(uploadPicture(picMap, rowIndex, 17));
                        add(license); // 证书一
                    }
                    ApplyLicense license1 = new ApplyLicense();
                    license1.setType(dto.getLicenseType1());
                    license1.setProject(dto.getLicenseProject1());
                    license1.setNumber(dto.getLicenseNumber1());
                    license1.setIssueDate(dto.getLicenseIssueDate1());
                    license1.setExpireDate(dto.getLicenseExpireDate1());
                    license1.setName(dto.getLicenseName1()); // 或从Excel中获取其他字段
                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(license1.getType())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license1.getProject())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license1.getName())
                    ) {
                        license1.setImgUrl(uploadPicture(picMap, rowIndex, 24));
                        add(license1); // 证书二
                    }
                    ApplyLicense license2 = new ApplyLicense();
                    license2.setType(dto.getLicenseType2());
                    license2.setProject(dto.getLicenseProject2());
                    license2.setNumber(dto.getLicenseNumber2());
                    license2.setIssueDate(dto.getLicenseIssueDate2());
                    license2.setExpireDate(dto.getLicenseExpireDate2());
                    license2.setName(dto.getLicenseName2()); // 或从Excel中获取其他字段
                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(license2.getType())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license2.getProject())
                            && org.apache.commons.lang3.StringUtils.isNotEmpty(license2.getName())
                    ) {
                        license2.setImgUrl(uploadPicture(picMap, rowIndex, 29));
                        add(license2); // 证书三
                    }
                }
            });

            // 处理图片
            apply.setPhotoUrl(uploadPicture(picMap, rowIndex, 8)); // sort=9, 对应列索引=8, 此时填入 col = 8
            apply.setHealthProofUrl(uploadPicture(picMap, rowIndex, 9));
            apply.setNoCriminalProofUrl(uploadPicture(picMap, rowIndex, 10));
//            apply.setCertificatePhotosUrl1(uploadPicture(picMap, rowIndex, 16));
//            apply.setCertificatePhotosUrl2(uploadPicture(picMap, rowIndex, 17));
//            apply.setCertificatePhotosUrl3(uploadPicture(picMap, rowIndex, 18));
            apply.setIsApply("3");
            log.error("入库：{}", JSON.toJSONString(apply));
            Long applyId = baseService.queryUserByUserId(apply);
            if (Objects.isNull(applyId)) { // 插入一条新的
                baseService.applyExam(apply, null); // 特殊处理,user 传入null;
            } else { // 更新原来的数据
                UpdateWrapper<ExamApply> updateWrapper = new UpdateWrapper<>();
                updateWrapper
                        .lambda()
                        .eq(ExamApply::getId, applyId)
                        .set(org.apache.commons.lang3.StringUtils.isNotEmpty(apply.getPhotoUrl()), ExamApply::getPhotoUrl, apply.getPhotoUrl())
                        .set(org.apache.commons.lang3.StringUtils.isNotEmpty(apply.getHealthProofUrl()), ExamApply::getHealthProofUrl, apply.getHealthProofUrl())
                        .set(org.apache.commons.lang3.StringUtils.isNotEmpty(apply.getNoCriminalProofUrl()), ExamApply::getNoCriminalProofUrl, apply.getNoCriminalProofUrl())
                        .set(!apply.getLicenses().isEmpty(), ExamApply::getLicenses, JSON.toJSONString(apply.getLicenses()))
                        .set(org.apache.commons.lang3.StringUtils.isNotEmpty(apply.getCompanyName()), ExamApply::getCompanyName, apply.getCompanyName())
                ;
                examApplyService.getBaseMapper().update(null, updateWrapper);
            }
        }
    }

    // 提取图片的方法
    private Map<String, XSSFPictureData> extractPictures(XSSFSheet sheet) {
        Map<String, XSSFPictureData> map = new HashMap<>();
        for (POIXMLDocumentPart dr : sheet.getRelations()) {
            if (dr instanceof XSSFDrawing) {
                XSSFDrawing drawing = (XSSFDrawing) dr;
                for (XSSFShape shape : drawing.getShapes()) {
                    if (shape instanceof XSSFPicture) {
                        XSSFPicture picture = (XSSFPicture) shape;
                        XSSFClientAnchor anchor = picture.getPreferredSize();
                        int row = anchor.getRow1();
                        int col = anchor.getCol1(); // 左锚点列数字
                        String key = row + "-" + col;
                        map.put(key, picture.getPictureData());
                    }
                }
            }
        }
        return map;
    }

    // 上传图片并返回URL
    private String uploadPicture(Map<String, XSSFPictureData> picMap, int row, int col) {
        String key = row + "-" + col;
        XSSFPictureData picData = picMap.get(key);
        if (Objects.isNull(picData)) {
            log.error("key:{} 无图片数据", key);
            return null;
        }
        try {
            log.info("上传图片：{}", key);
            byte[] data = picData.getData();
            String ext = picData.suggestFileExtension();
            MultipartFile file = new Base64DecodedMultipartFile(data, "upload." + ext, "image/" + ext);
            UploadReqDTO uploadReqDTO = new UploadReqDTO();
            uploadReqDTO.setFile(file);
            UploadRespDTO respDTO = uploadService.upload(uploadReqDTO);
            return respDTO.getUrl();
        } catch (Exception e) {
            log.error("上传失败", e);
            return null;
        }
    }

    private String getCellString(Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING:
                return cell.getStringCellValue().trim();
            case Cell.CELL_TYPE_NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cell.getDateCellValue());
                } else {
                    return String.valueOf((long) cell.getNumericCellValue());
                }
            case Cell.CELL_TYPE_BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case Cell.CELL_TYPE_FORMULA:
                return cell.getCellFormula();
            default:
                return null;
        }
    }


    /**
     * 处理回答列表
     *
     * @param importList
     * @return
     */
    private List<QuAnswerDTO> processAnswerList(List<QuExportDTO> importList) {

        List<QuAnswerDTO> list = new ArrayList<>(16);
        for (QuExportDTO item : importList) {
            QuAnswerDTO a = new QuAnswerDTO();
            a.setIsRight("1".equals(item.getAIsRight()));
            a.setContent(item.getAContent());
            a.setAnalysis(item.getAAnalysis());
            a.setId("");
            list.add(a);
        }
        return list;
    }

    /**
     * 校验题目信息
     *
     * @param qu
     * @param no
     * @throws Exception
     */
    public void checkData(QuDetailDTO qu, String no) {


        if (StringUtils.isEmpty(qu.getContent())) {
            throw new ServiceException(1, no + "题目内容不能为空！");
        }


        if (CollectionUtils.isEmpty(qu.getRepoIds())) {
            throw new ServiceException(1, no + "至少要选择一个题库！");
        }

        List<QuAnswerDTO> answers = qu.getAnswerList();


        if (CollectionUtils.isEmpty(answers)) {
            throw new ServiceException(1, no + "客观题至少要包含一个备选答案！");
        }


        int trueCount = 0;
        for (QuAnswerDTO a : answers) {

            if (a.getIsRight() == null) {
                throw new ServiceException(1, no + "必须定义选项是否正确项！");
            }

            if (StringUtils.isEmpty(a.getContent())) {
                throw new ServiceException(1, no + "选项内容不为空！");
            }

            if (a.getIsRight()) {
                trueCount += 1;
            }
        }

        if (trueCount == 0) {
            throw new ServiceException(1, no + "至少要包含一个正确项！");
        }


        //单选题
        if (qu.getQuType().equals(QuType.RADIO) && trueCount > 1) {
            throw new ServiceException(1, no + "单选题不能包含多个正确项！");
        }

    }
}
